package com.gdut.huayan.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.gdut.huayan.constant.WechatConstant;
import com.gdut.huayan.entity.po.Order;
import com.gdut.huayan.entity.po.Refund;
import com.gdut.huayan.service.WechatService;
import com.gdut.huayan.service.exception.BaseException;
import com.gdut.huayan.utils.RedisUtils;
import com.gdut.huayan.utils.WebJsonUtils;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.util.*;


@Service
@Slf4j
public class WechatServiceImpl implements WechatService {

    @Autowired
    private WebJsonUtils webJsonUtils;


    private static WechatPayHttpClientBuilder builder;

    private static Verifier verifier;

//    static {
//        try {
//            // 获取证书管理器实例
//            CertificatesManager certificatesManager = CertificatesManager.getInstance();
//            // 向证书管理器增加需要自动更新平台证书的商户信息
//            certificatesManager.putMerchant(WechatConstant.MCH_ID, new WechatPay2Credentials(WechatConstant.MCH_ID,
//                            new PrivateKeySigner(WechatConstant.MCH_SERIAL_NUMBER, WechatConstant.MCH_PRIVATE_KEY)),
//                    WechatConstant.API_V3_KEY.getBytes(StandardCharsets.UTF_8));
//            // 从证书管理器中获取verifier
//            verifier = certificatesManager.getVerifier(WechatConstant.MCH_ID);
//            builder = WechatPayHttpClientBuilder.create()
//                    .withMerchant(WechatConstant.MCH_ID, WechatConstant.MCH_SERIAL_NUMBER, WechatConstant.MCH_PRIVATE_KEY)
//                    .withValidator(new WechatPay2Validator(verifier));
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

    @Override
    public void getAccessToken() {
        //有效期目前为 2 个小时，需定时刷新，
        JSONObject json = webJsonUtils.sendGet(WechatConstant.GET_TOKEN_URL);
        String token = json.getString("access_token");
        if (token == null || "".equals(token)) {
            log.warn("未获取到token，错误信息:{}", json.getString("errmsg"));
        } else {
            log.info("从微信服务器获取到AccessToken:\n{}", token);
            WechatConstant.ACCESS_TOKEN = token;
        }
    }

    private String sign(byte[] message) throws
            NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(WechatConstant.MCH_PRIVATE_KEY);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    @Override
    public Map<String, String> prepayOrder(Order order) {
        CloseableHttpClient httpClient = builder.build();
        try {
            HttpPost httpPost = new HttpPost(WechatConstant.PAY_URL);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid", WechatConstant.MCH_ID)
                    .put("appid", WechatConstant.APP_ID)
                    .put("description", order.getRemark())
                    .put("notify_url", WechatConstant.NOTIFY_URL)
                    .put("out_trade_no", order.getOrderNumber());
            rootNode.putObject("amount")
                    .put("total", order.getMoneyAmount())
                    .put("currency", "CNY");
            rootNode.putObject("payer")
                    .put("openid", order.getOpenId());
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString(StandardCharsets.UTF_8), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            JSONObject jsonObject = JSONObject.parseObject(bodyAsString);
            order.setPrepayId(jsonObject.getString("prepay_id"));//
            String timeStamp = new Date().getTime() + "";
            String nonce = UUID.randomUUID().toString().replace("-", "");
            String builder = WechatConstant.APP_ID + "\n" +
                    timeStamp + "\n" +
                    nonce + "\n" +
                    "prepay_id=" + order.getPrepayId() + "\n";
            String sign = sign(builder.getBytes(StandardCharsets.UTF_8));
            Map<String, String> result = new HashMap<>();
            result.put("timeStamp", timeStamp);
            result.put("nonceStr", nonce);
            result.put("package", "prepay_id=" + order.getPrepayId());
            result.put("paySign", sign);

            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(4007, "订单出错，下单失败");
        } finally {
            closeClient(httpClient);
        }
    }


    @Override
    public Map<String,String> parseCallback(HttpServletRequest request) throws Exception{
        BufferedReader br = request.getReader();
        String str;
        StringBuilder builder = new StringBuilder();
        while ((str = br.readLine()) != null) {
            builder.append(str);
        }
        //参数 1.微信序列号 2.应答随机串 3.应答时间戳 4.应答签名 5.应答报文主体
        NotificationRequest notificationRequest = new NotificationRequest.Builder()
                .withSerialNumber(request.getHeader(WechatConstant.SERIAL))
                .withNonce(request.getHeader(WechatConstant.NONCE))
                .withTimestamp(request.getHeader(WechatConstant.TIMESTAMP))
                .withSignature(request.getHeader(WechatConstant.SIGNATURE))
                .withBody(builder.toString())
                .build();
        NotificationHandler handler = new NotificationHandler(verifier, WechatConstant.API_V3_KEY.getBytes(StandardCharsets.UTF_8));
        // 验签和解析请求体
        Notification notification = handler.parse(notificationRequest);
        JSONObject jsonObject = JSONObject.parseObject(notification.getDecryptData());
        log.warn(jsonObject.toJSONString());
        String orderNumber = jsonObject.getString("out_trade_no");
        String state = jsonObject.getString("trade_state");
        return Map.of("orderNumber",orderNumber,"status",state);
    }

    @Override
    public Map<String,String> refundCallback(HttpServletRequest request) throws Exception{
        BufferedReader br = request.getReader();
        String str;
        StringBuilder builder = new StringBuilder();
        while ((str = br.readLine()) != null) {
            builder.append(str);
        }
        //参数 1.微信序列号 2.应答随机串 3.应答时间戳 4.应答签名 5.应答报文主体
        NotificationRequest notificationRequest = new NotificationRequest.Builder()
                .withSerialNumber(request.getHeader(WechatConstant.SERIAL))
                .withNonce(request.getHeader(WechatConstant.NONCE))
                .withTimestamp(request.getHeader(WechatConstant.TIMESTAMP))
                .withSignature(request.getHeader(WechatConstant.SIGNATURE))
                .withBody(builder.toString())
                .build();
        NotificationHandler handler = new NotificationHandler(verifier, WechatConstant.API_V3_KEY.getBytes(StandardCharsets.UTF_8));
        // 验签和解析请求体
        Notification notification = handler.parse(notificationRequest);
        JSONObject jsonObject = JSONObject.parseObject(notification.getDecryptData());
        log.warn(jsonObject.toJSONString());
        String orderNumber = jsonObject.getString("out_trade_no");
        String refundNumber = jsonObject.getString("out_refund_no");
        String state = jsonObject.getString("refund_status");
        String successTime = jsonObject.getString("success_time");
        String payerRefund = jsonObject.getJSONObject("amount").getString("payer_refund");
        return Map.of("orderNumber",orderNumber,"refundNumber",refundNumber,"status",state,"successTime",successTime,"payerRefund",payerRefund);
    }

    @Override
    public JSONObject orderRefund(Refund refund,Order order) {
        CloseableHttpClient httpClient = builder.build();
        HttpPost httpPost = new HttpPost(WechatConstant.REFUND_URL);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("out_trade_no",refund.getOrderNumber());
        rootNode.put("out_refund_no",refund.getRefundNumber());
        rootNode.put("notify_url",WechatConstant.REFUND_CALL_URL);
        /*if (StringUtils.isNotEmpty(refund.getReason())){
            rootNode.put("reason",refund.getReason());
        }*/
        rootNode.putObject("amount")
                .put("refund",refund.getActualMoneyAmount())
                .put("total",order.getMoneyAmount())
                .put("currency","CNY");

        try{
            objectMapper.writeValue(bos,rootNode);
            httpPost.setEntity(new StringEntity(bos.toString(StandardCharsets.UTF_8), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return JSONObject.parseObject(bodyAsString);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(4000,"退款操作失败，请联系技术人员");
        } finally {
            closeClient(httpClient);
        }


    }

    private void closeClient(CloseableHttpClient httpClient){
        if (httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

}